package com.xmmj.baselibrary.network

import android.annotation.SuppressLint
import java.io.File
import java.io.IOException
import java.lang.Exception
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager

/**
 * https证书认证工具类
 */
class HttpsUtil private constructor() {

    companion object {
        private var mHttpsUtil: HttpsUtil? = null

        val instance: HttpsUtil?
            get() {
                if (mHttpsUtil == null) mHttpsUtil = HttpsUtil()
                return mHttpsUtil
            }
    }

    /**
     * 设置证书
     */
    fun setCertificates(file: File): SSLSocketFactory? {
        try {
            //读取证书
            val inputStream = file.inputStream()
            //根据公钥证书生产Certificate对象
            val certificateFactory = CertificateFactory.getInstance("X.509")
            val certificate = certificateFactory.generateCertificate(inputStream)
            //创建一个证书库，并将证书导入证书库
            val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
            keyStore.load(null)
            keyStore.setCertificateEntry("custom", certificate)
            //及时关闭输入流
            try {
                inputStream.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            // 创建一个证书管理器，并将证书库导入
            val trustManagerFactory =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
            trustManagerFactory.init(keyStore)
            // 创建一个使用我们证书管理器的SSLContext
            val sslContext = SSLContext.getInstance("SSL")
            sslContext.init(null, trustManagerFactory.trustManagers, SecureRandom())
            return sslContext.socketFactory
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    /**
     * 证书认证管理器
     */
    @SuppressLint("CustomX509TrustManager", "TrustAllX509TrustManager")
    val x509TrustManager: X509TrustManager = object : X509TrustManager {
        /**
         * 检验客户端证书
         * @param chain 证书内容
         * @param authType 授权类型
         */
        @SuppressLint("TrustAllX509TrustManager")
        override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
            //不做处理
        }

        /**
         * 检验服务端证书
         * @param chain 证书内容
         * @param authType 授权类型
         */
        @SuppressLint("TrustAllX509TrustManager")
        override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
            //不做处理，信任任意证书的风险：
            //如果用户手机中安装了一个恶意证书，那么就可以通过中间人攻击的方式进行窃听用户通信以及修改request或者response中的数据；
            //在钓鱼Wifi网络中，同样地，攻击者可以通过设置DNS服务器使客户端与指定的服务器进行通信。攻击者在服务器上部署另一个证书，
            //在会话建立阶段，客户端会收到这张证书，如果客户端忽略这个证书上的异常，或者接受这个证书，就会成功建立会话、开始加密通信。
            //但攻击者拥有私钥，因此可以解密得到客户端发来的数据明文。接着，攻击者可以模拟客户端，与真正的服务器联系，充当中间人做监听。

            //保护办法：
            //1 公钥锁定:将证书公钥写入客户端代码中，https通信时检查服务端传输的证书公钥与代码中的公钥是否一致。（在checkServerTrusted
            // 该方法中进行比较判断）
            //2 证书锁定:即为客户端颁发公钥证书存放在手机客户端中（使用keystore），在https通信时，在客户端代码中固定去取证书信息，不是
            // 从服务端中获取。

            //本工具类采取第2种方式，因此在该回调中未做任何代码处理，但是在apk做安全监测时可能依然会提示这个风险，我们可以忽略
        }

        /**
         * 返回受信任的X509证书数组
         */
        override fun getAcceptedIssuers(): Array<X509Certificate?> {
            return arrayOfNulls(0)
        }
    }
}